feat(codegen): collected video duration samples
This commit is contained in:
parent
19781eab36
commit
800073df48
14 changed files with 7325 additions and 1449 deletions
2
Justfile
2
Justfile
|
|
@ -34,7 +34,7 @@ testintl:
|
||||||
done
|
done
|
||||||
|
|
||||||
testfiles:
|
testfiles:
|
||||||
cargo run -p rustypipe-codegen -- -d . download-testfiles
|
cargo run -p rustypipe-codegen download-testfiles
|
||||||
|
|
||||||
report2yaml:
|
report2yaml:
|
||||||
mkdir -p rustypipe_reports/conv
|
mkdir -p rustypipe_reports/conv
|
||||||
|
|
|
||||||
|
|
@ -19,5 +19,7 @@ phf_codegen = "0.11.1"
|
||||||
once_cell = "1.12.0"
|
once_cell = "1.12.0"
|
||||||
regex = "1.7.1"
|
regex = "1.7.1"
|
||||||
indicatif = "0.17.0"
|
indicatif = "0.17.0"
|
||||||
num_enum = "0.5.7"
|
num_enum = "0.6.1"
|
||||||
path_macro = "1.0.0"
|
path_macro = "1.0.0"
|
||||||
|
intl_pluralrules = "7.0.2"
|
||||||
|
unic-langid = "0.9.1"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path};
|
use std::{collections::BTreeMap, fs::File, io::BufReader};
|
||||||
|
|
||||||
use futures::stream::{self, StreamExt};
|
use futures::stream::{self, StreamExt};
|
||||||
use path_macro::path;
|
use path_macro::path;
|
||||||
|
|
@ -9,10 +9,13 @@ use rustypipe::{
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::util::{self, QBrowse, TextRuns};
|
use crate::{
|
||||||
|
model::{QBrowse, TextRuns},
|
||||||
|
util::{self, DICT_DIR},
|
||||||
|
};
|
||||||
|
|
||||||
pub async fn collect_album_types(project_root: &Path, concurrency: usize) {
|
pub async fn collect_album_types(concurrency: usize) {
|
||||||
let json_path = path!(project_root / "testfiles" / "dict" / "album_type_samples.json");
|
let json_path = path!(*DICT_DIR / "album_type_samples.json");
|
||||||
|
|
||||||
let album_types = [
|
let album_types = [
|
||||||
(AlbumType::Album, "MPREb_nlBWQROfvjo"),
|
(AlbumType::Album, "MPREb_nlBWQROfvjo"),
|
||||||
|
|
@ -48,13 +51,13 @@ pub async fn collect_album_types(project_root: &Path, concurrency: usize) {
|
||||||
serde_json::to_writer_pretty(file, &collected_album_types).unwrap();
|
serde_json::to_writer_pretty(file, &collected_album_types).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_samples_to_dict(project_root: &Path) {
|
pub fn write_samples_to_dict() {
|
||||||
let json_path = path!(project_root / "testfiles" / "dict" / "album_type_samples.json");
|
let json_path = path!(*DICT_DIR / "album_type_samples.json");
|
||||||
|
|
||||||
let json_file = File::open(json_path).unwrap();
|
let json_file = File::open(json_path).unwrap();
|
||||||
let collected: BTreeMap<Language, BTreeMap<AlbumType, String>> =
|
let collected: BTreeMap<Language, BTreeMap<AlbumType, String>> =
|
||||||
serde_json::from_reader(BufReader::new(json_file)).unwrap();
|
serde_json::from_reader(BufReader::new(json_file)).unwrap();
|
||||||
let mut dict = util::read_dict(project_root);
|
let mut dict = util::read_dict();
|
||||||
let langs = dict.keys().map(|k| k.to_owned()).collect::<Vec<_>>();
|
let langs = dict.keys().map(|k| k.to_owned()).collect::<Vec<_>>();
|
||||||
|
|
||||||
for lang in langs {
|
for lang in langs {
|
||||||
|
|
@ -72,7 +75,7 @@ pub fn write_samples_to_dict(project_root: &Path) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
util::write_dict(project_root, dict);
|
util::write_dict(dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ use std::{
|
||||||
collections::{BTreeMap, HashMap, HashSet},
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
fs::File,
|
fs::File,
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
@ -14,9 +13,13 @@ use regex::Regex;
|
||||||
use rustypipe::client::{ClientType, RustyPipe, RustyPipeQuery};
|
use rustypipe::client::{ClientType, RustyPipe, RustyPipeQuery};
|
||||||
use rustypipe::param::{locale::LANGUAGES, Language};
|
use rustypipe::param::{locale::LANGUAGES, Language};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_with::{serde_as, DefaultOnError, VecSkipError};
|
|
||||||
|
|
||||||
use crate::util::{self, QBrowse, QCont, Text, TextRuns};
|
use crate::model::{Channel, ContinuationResponse};
|
||||||
|
use crate::util::DICT_DIR;
|
||||||
|
use crate::{
|
||||||
|
model::{QBrowse, QCont, TextRuns},
|
||||||
|
util,
|
||||||
|
};
|
||||||
|
|
||||||
type CollectedNumbers = BTreeMap<Language, BTreeMap<String, u64>>;
|
type CollectedNumbers = BTreeMap<Language, BTreeMap<String, u64>>;
|
||||||
|
|
||||||
|
|
@ -34,8 +37,8 @@ type CollectedNumbers = BTreeMap<Language, BTreeMap<String, u64>>;
|
||||||
/// We extract these instead of subscriber counts because the YouTube API
|
/// We extract these instead of subscriber counts because the YouTube API
|
||||||
/// outputs view counts both in approximated and exact format, so we can use
|
/// outputs view counts both in approximated and exact format, so we can use
|
||||||
/// the exact counts to figure out the tokens.
|
/// the exact counts to figure out the tokens.
|
||||||
pub async fn collect_large_numbers(project_root: &Path, concurrency: usize) {
|
pub async fn collect_large_numbers(concurrency: usize) {
|
||||||
let json_path = path!(project_root / "testfiles" / "dict" / "large_number_samples_all.json");
|
let json_path = path!(*DICT_DIR / "large_number_samples_all.json");
|
||||||
let rp = RustyPipe::new();
|
let rp = RustyPipe::new();
|
||||||
|
|
||||||
let channels = [
|
let channels = [
|
||||||
|
|
@ -137,13 +140,13 @@ pub async fn collect_large_numbers(project_root: &Path, concurrency: usize) {
|
||||||
|
|
||||||
/// Attempt to parse the numbers collected by `collect-large-numbers`
|
/// Attempt to parse the numbers collected by `collect-large-numbers`
|
||||||
/// and write the results to `dictionary.json`.
|
/// and write the results to `dictionary.json`.
|
||||||
pub fn write_samples_to_dict(project_root: &Path) {
|
pub fn write_samples_to_dict() {
|
||||||
let json_path = path!(project_root / "testfiles" / "dict" / "large_number_samples.json");
|
let json_path = path!(*DICT_DIR / "large_number_samples.json");
|
||||||
|
|
||||||
let json_file = File::open(json_path).unwrap();
|
let json_file = File::open(json_path).unwrap();
|
||||||
let collected_nums: CollectedNumbers =
|
let collected_nums: CollectedNumbers =
|
||||||
serde_json::from_reader(BufReader::new(json_file)).unwrap();
|
serde_json::from_reader(BufReader::new(json_file)).unwrap();
|
||||||
let mut dict = util::read_dict(project_root);
|
let mut dict = util::read_dict();
|
||||||
let langs = dict.keys().map(|k| k.to_owned()).collect::<Vec<_>>();
|
let langs = dict.keys().map(|k| k.to_owned()).collect::<Vec<_>>();
|
||||||
|
|
||||||
static POINT_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\d(\.|,)\d{1,3}(?:\D|$)").unwrap());
|
static POINT_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\d(\.|,)\d{1,3}(?:\D|$)").unwrap());
|
||||||
|
|
@ -292,164 +295,13 @@ pub fn write_samples_to_dict(project_root: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
util::write_dict(project_root, dict);
|
util::write_dict(dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mag(n: u64) -> u8 {
|
fn get_mag(n: u64) -> u8 {
|
||||||
(n as f64).log10().floor() as u8
|
(n as f64).log10().floor() as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
YouTube channel videos response
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct Channel {
|
|
||||||
contents: Contents,
|
|
||||||
header: ChannelHeader,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct ChannelHeader {
|
|
||||||
c4_tabbed_header_renderer: HeaderRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct HeaderRenderer {
|
|
||||||
subscriber_count_text: Text,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct Contents {
|
|
||||||
two_column_browse_results_renderer: TabsRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[serde_as]
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct TabsRenderer {
|
|
||||||
#[serde_as(as = "VecSkipError<_>")]
|
|
||||||
tabs: Vec<TabRendererWrap>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct TabRendererWrap {
|
|
||||||
tab_renderer: TabRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct TabRenderer {
|
|
||||||
content: RichGridRendererWrap,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct RichGridRendererWrap {
|
|
||||||
rich_grid_renderer: RichGridRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[serde_as]
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct RichGridRenderer {
|
|
||||||
#[serde_as(as = "VecSkipError<_>")]
|
|
||||||
contents: Vec<RichItemRendererWrap>,
|
|
||||||
#[serde(default)]
|
|
||||||
#[serde_as(as = "DefaultOnError")]
|
|
||||||
header: Option<RichGridHeader>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct RichItemRendererWrap {
|
|
||||||
rich_item_renderer: RichItemRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct RichItemRenderer {
|
|
||||||
content: VideoRendererWrap,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct VideoRendererWrap {
|
|
||||||
video_renderer: VideoRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct VideoRenderer {
|
|
||||||
/// `24,194 views`
|
|
||||||
view_count_text: Text,
|
|
||||||
/// `19K views`
|
|
||||||
short_view_count_text: Text,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct RichGridHeader {
|
|
||||||
feed_filter_chip_bar_renderer: ChipBar,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct ChipBar {
|
|
||||||
contents: Vec<Chip>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct Chip {
|
|
||||||
chip_cloud_chip_renderer: ChipRenderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct ChipRenderer {
|
|
||||||
navigation_endpoint: NavigationEndpoint,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct NavigationEndpoint {
|
|
||||||
continuation_command: ContinuationCommand,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct ContinuationCommand {
|
|
||||||
token: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[serde_as]
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct ContinuationResponse {
|
|
||||||
// #[serde_as(as = "VecSkipError<_>")]
|
|
||||||
on_response_received_actions: Vec<ContinuationAction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct ContinuationAction {
|
|
||||||
reload_continuation_items_command: ContinuationItemsWrap,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[serde_as]
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct ContinuationItemsWrap {
|
|
||||||
#[serde_as(as = "VecSkipError<_>")]
|
|
||||||
continuation_items: Vec<RichItemRendererWrap>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
YouTube Music channel data
|
YouTube Music channel data
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures::{stream, StreamExt};
|
use futures::{stream, StreamExt};
|
||||||
|
|
@ -11,11 +10,10 @@ use path_macro::path;
|
||||||
use rustypipe::{
|
use rustypipe::{
|
||||||
client::RustyPipe,
|
client::RustyPipe,
|
||||||
param::{locale::LANGUAGES, Language},
|
param::{locale::LANGUAGES, Language},
|
||||||
timeago::{self, TimeAgo},
|
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::util;
|
use crate::util::{self, DICT_DIR};
|
||||||
|
|
||||||
type CollectedDates = BTreeMap<Language, BTreeMap<DateCase, String>>;
|
type CollectedDates = BTreeMap<Language, BTreeMap<DateCase, String>>;
|
||||||
|
|
||||||
|
|
@ -38,8 +36,6 @@ enum DateCase {
|
||||||
Dec,
|
Dec,
|
||||||
}
|
}
|
||||||
|
|
||||||
const N_AGO: u8 = 5;
|
|
||||||
|
|
||||||
/// Collect 'Playlist updated' dates in every supported language
|
/// Collect 'Playlist updated' dates in every supported language
|
||||||
/// and write them to `testfiles/dict/playlist_samples.json`.
|
/// and write them to `testfiles/dict/playlist_samples.json`.
|
||||||
///
|
///
|
||||||
|
|
@ -64,8 +60,8 @@ const N_AGO: u8 = 5;
|
||||||
///
|
///
|
||||||
/// Because the relative dates change with time, the first three playlists
|
/// Because the relative dates change with time, the first three playlists
|
||||||
/// have to checked and eventually changed before running the program.
|
/// have to checked and eventually changed before running the program.
|
||||||
pub async fn collect_dates(project_root: &Path, concurrency: usize) {
|
pub async fn collect_dates(concurrency: usize) {
|
||||||
let json_path = path!(project_root / "testfiles" / "dict" / "playlist_samples.json");
|
let json_path = path!(*DICT_DIR / "playlist_samples.json");
|
||||||
|
|
||||||
// These are the sample playlists
|
// These are the sample playlists
|
||||||
let cases = [
|
let cases = [
|
||||||
|
|
@ -115,13 +111,13 @@ pub async fn collect_dates(project_root: &Path, concurrency: usize) {
|
||||||
///
|
///
|
||||||
/// The ND (no digit) tokens (today, tomorrow) of some languages cannot be
|
/// The ND (no digit) tokens (today, tomorrow) of some languages cannot be
|
||||||
/// parsed automatically and require manual work.
|
/// parsed automatically and require manual work.
|
||||||
pub fn write_samples_to_dict(project_root: &Path) {
|
pub fn write_samples_to_dict() {
|
||||||
let json_path = path!(project_root / "testfiles" / "dict" / "playlist_samples.json");
|
let json_path = path!(*DICT_DIR / "playlist_samples.json");
|
||||||
|
|
||||||
let json_file = File::open(json_path).unwrap();
|
let json_file = File::open(json_path).unwrap();
|
||||||
let collected_dates: CollectedDates =
|
let collected_dates: CollectedDates =
|
||||||
serde_json::from_reader(BufReader::new(json_file)).unwrap();
|
serde_json::from_reader(BufReader::new(json_file)).unwrap();
|
||||||
let mut dict = util::read_dict(project_root);
|
let mut dict = util::read_dict();
|
||||||
let langs = dict.keys().map(|k| k.to_owned()).collect::<Vec<_>>();
|
let langs = dict.keys().map(|k| k.to_owned()).collect::<Vec<_>>();
|
||||||
|
|
||||||
let months = [
|
let months = [
|
||||||
|
|
@ -200,20 +196,6 @@ pub fn write_samples_to_dict(project_root: &Path) {
|
||||||
parse(datestr_table.get(&DateCase::Jan).unwrap(), 0);
|
parse(datestr_table.get(&DateCase::Jan).unwrap(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// n days ago
|
|
||||||
{
|
|
||||||
let datestr = datestr_table.get(&DateCase::Ago).unwrap();
|
|
||||||
let tago = timeago::parse_timeago(lang, datestr);
|
|
||||||
assert_eq!(
|
|
||||||
tago,
|
|
||||||
Some(TimeAgo {
|
|
||||||
n: N_AGO,
|
|
||||||
unit: timeago::TimeUnit::Day
|
|
||||||
}),
|
|
||||||
"lang: {lang}, txt: {datestr}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Absolute dates (Jan 3, 2020)
|
// Absolute dates (Jan 3, 2020)
|
||||||
months.iter().enumerate().for_each(|(n, m)| {
|
months.iter().enumerate().for_each(|(n, m)| {
|
||||||
let datestr = datestr_table.get(m).unwrap();
|
let datestr = datestr_table.get(m).unwrap();
|
||||||
|
|
@ -291,5 +273,5 @@ pub fn write_samples_to_dict(project_root: &Path) {
|
||||||
dict_entry.date_order = num_order;
|
dict_entry.date_order = num_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::write_dict(project_root, dict);
|
util::write_dict(dict);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
176
codegen/src/collect_video_durations.rs
Normal file
176
codegen/src/collect_video_durations.rs
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
use std::{collections::BTreeMap, fs::File};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use futures::{stream, StreamExt};
|
||||||
|
use path_macro::path;
|
||||||
|
use rustypipe::{
|
||||||
|
client::{ClientType, RustyPipe, RustyPipeQuery},
|
||||||
|
param::{locale::LANGUAGES, Language},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
model::{Channel, QBrowse},
|
||||||
|
util::{self, DICT_DIR},
|
||||||
|
};
|
||||||
|
|
||||||
|
type CollectedDurations = BTreeMap<Language, BTreeMap<String, u32>>;
|
||||||
|
|
||||||
|
/// Collect the video duration texts in every supported language
|
||||||
|
/// and write them to `testfiles/dict/video_duration_samples.json`.
|
||||||
|
///
|
||||||
|
/// The length of YouTube short videos is only available in textual form.
|
||||||
|
/// To parse it correctly, we need to collect samples of this text in every
|
||||||
|
/// language. We collect these samples from regular channel videos because these
|
||||||
|
/// include a textual duration in addition to the easy to parse "mm:ss"
|
||||||
|
/// duration format.
|
||||||
|
pub async fn collect_video_durations(concurrency: usize) {
|
||||||
|
let json_path = path!(*DICT_DIR / "video_duration_samples.json");
|
||||||
|
let rp = RustyPipe::new();
|
||||||
|
|
||||||
|
let channels = [
|
||||||
|
"UCq-Fj5jknLsUf-MWSy4_brA",
|
||||||
|
"UCMcS5ITpSohfr8Ppzlo4vKw",
|
||||||
|
"UCXuqSBlHAE6Xw-yeJA0Tunw",
|
||||||
|
];
|
||||||
|
|
||||||
|
let durations: CollectedDurations = stream::iter(LANGUAGES)
|
||||||
|
.map(|lang| {
|
||||||
|
let rp = rp.query().lang(lang);
|
||||||
|
async move {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
|
||||||
|
for (n, ch_id) in channels.iter().enumerate() {
|
||||||
|
get_channel_vlengths(&rp, ch_id, &mut map).await.unwrap();
|
||||||
|
println!("collected {lang}-{n}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we are only parsing shorts durations, we do not need durations >= 1h
|
||||||
|
let map = map.into_iter().filter(|(_, v)| v < &3600).collect();
|
||||||
|
(lang, map)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.buffer_unordered(concurrency)
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let file = File::create(json_path).unwrap();
|
||||||
|
serde_json::to_writer_pretty(file, &durations).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_channel_vlengths(
|
||||||
|
query: &RustyPipeQuery,
|
||||||
|
channel_id: &str,
|
||||||
|
map: &mut BTreeMap<String, u32>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let resp = query
|
||||||
|
.raw(
|
||||||
|
ClientType::Desktop,
|
||||||
|
"browse",
|
||||||
|
&QBrowse {
|
||||||
|
context: query.get_context(ClientType::Desktop, true, None).await,
|
||||||
|
browse_id: channel_id,
|
||||||
|
params: Some("EgZ2aWRlb3MYASAAMAE"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let channel = serde_json::from_str::<Channel>(&resp)?;
|
||||||
|
|
||||||
|
let tab = channel
|
||||||
|
.contents
|
||||||
|
.two_column_browse_results_renderer
|
||||||
|
.tabs
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.tab_renderer
|
||||||
|
.content
|
||||||
|
.rich_grid_renderer;
|
||||||
|
|
||||||
|
tab.contents.into_iter().for_each(|c| {
|
||||||
|
let lt = c.rich_item_renderer.content.video_renderer.length_text;
|
||||||
|
let duration = util::parse_video_length(<.simple_text).unwrap();
|
||||||
|
map.insert(lt.accessibility.accessibility_data.label, duration);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
enum PluralCategory {
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
Few,
|
||||||
|
Many,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<intl_pluralrules::PluralCategory> for PluralCategory {
|
||||||
|
fn from(value: intl_pluralrules::PluralCategory) -> Self {
|
||||||
|
match value {
|
||||||
|
intl_pluralrules::PluralCategory::ZERO => Self::Zero,
|
||||||
|
intl_pluralrules::PluralCategory::ONE => Self::One,
|
||||||
|
intl_pluralrules::PluralCategory::TWO => Self::Two,
|
||||||
|
intl_pluralrules::PluralCategory::FEW => Self::Few,
|
||||||
|
intl_pluralrules::PluralCategory::MANY => Self::Many,
|
||||||
|
intl_pluralrules::PluralCategory::OTHER => Self::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::io::BufReader;
|
||||||
|
|
||||||
|
use intl_pluralrules::{PluralRuleType, PluralRules};
|
||||||
|
use unic_langid::LanguageIdentifier;
|
||||||
|
|
||||||
|
fn split_duration(d: u32) -> (u32, u32) {
|
||||||
|
(d / 60, d % 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify that the duration sample set covers all pluralization variants of the languages
|
||||||
|
#[test]
|
||||||
|
fn check_video_duration_samples() {
|
||||||
|
let json_path = path!(*DICT_DIR / "video_duration_samples.json");
|
||||||
|
let json_file = File::open(json_path).unwrap();
|
||||||
|
let durations: CollectedDurations =
|
||||||
|
serde_json::from_reader(BufReader::new(json_file)).unwrap();
|
||||||
|
let mut failed = false;
|
||||||
|
|
||||||
|
for (lang, durations) in durations {
|
||||||
|
let ul: LanguageIdentifier =
|
||||||
|
lang.to_string().split('-').next().unwrap().parse().unwrap();
|
||||||
|
|
||||||
|
let pr = PluralRules::create(ul, PluralRuleType::CARDINAL).expect(&lang.to_string());
|
||||||
|
|
||||||
|
let mut plurals_m: HashSet<PluralCategory> = HashSet::new();
|
||||||
|
for n in 1..60 {
|
||||||
|
plurals_m.insert(pr.select(n).unwrap().into());
|
||||||
|
}
|
||||||
|
let mut plurals_s = plurals_m.clone();
|
||||||
|
|
||||||
|
durations.values().for_each(|v| {
|
||||||
|
let (m, s) = split_duration(*v);
|
||||||
|
plurals_m.remove(&pr.select(m).unwrap().into());
|
||||||
|
plurals_s.remove(&pr.select(s).unwrap().into());
|
||||||
|
});
|
||||||
|
|
||||||
|
if !plurals_m.is_empty() {
|
||||||
|
println!("{lang}: missing minutes {plurals_m:?}");
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !plurals_s.is_empty() {
|
||||||
|
println!("{lang}: missing seconds {plurals_m:?}");
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(!failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ use std::{
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use path_macro::path;
|
||||||
use rustypipe::{
|
use rustypipe::{
|
||||||
client::{ClientType, RustyPipe},
|
client::{ClientType, RustyPipe},
|
||||||
param::{
|
param::{
|
||||||
|
|
@ -14,55 +15,54 @@ use rustypipe::{
|
||||||
report::{Report, Reporter},
|
report::{Report, Reporter},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn download_testfiles(project_root: &Path) {
|
use crate::util::TESTFILES_DIR;
|
||||||
let mut testfiles = project_root.to_path_buf();
|
|
||||||
testfiles.push("testfiles");
|
|
||||||
|
|
||||||
player(&testfiles).await;
|
pub async fn download_testfiles() {
|
||||||
player_model(&testfiles).await;
|
player().await;
|
||||||
playlist(&testfiles).await;
|
player_model().await;
|
||||||
playlist_cont(&testfiles).await;
|
playlist().await;
|
||||||
video_details(&testfiles).await;
|
playlist_cont().await;
|
||||||
comments_top(&testfiles).await;
|
video_details().await;
|
||||||
comments_latest(&testfiles).await;
|
comments_top().await;
|
||||||
recommendations(&testfiles).await;
|
comments_latest().await;
|
||||||
channel_videos(&testfiles).await;
|
recommendations().await;
|
||||||
channel_shorts(&testfiles).await;
|
channel_videos().await;
|
||||||
channel_livestreams(&testfiles).await;
|
channel_shorts().await;
|
||||||
channel_playlists(&testfiles).await;
|
channel_livestreams().await;
|
||||||
channel_info(&testfiles).await;
|
channel_playlists().await;
|
||||||
channel_videos_cont(&testfiles).await;
|
channel_info().await;
|
||||||
channel_playlists_cont(&testfiles).await;
|
channel_videos_cont().await;
|
||||||
search(&testfiles).await;
|
channel_playlists_cont().await;
|
||||||
search_cont(&testfiles).await;
|
search().await;
|
||||||
search_playlists(&testfiles).await;
|
search_cont().await;
|
||||||
search_empty(&testfiles).await;
|
search_playlists().await;
|
||||||
startpage(&testfiles).await;
|
search_empty().await;
|
||||||
startpage_cont(&testfiles).await;
|
startpage().await;
|
||||||
trending(&testfiles).await;
|
startpage_cont().await;
|
||||||
|
trending().await;
|
||||||
|
|
||||||
music_playlist(&testfiles).await;
|
music_playlist().await;
|
||||||
music_playlist_cont(&testfiles).await;
|
music_playlist_cont().await;
|
||||||
music_playlist_related(&testfiles).await;
|
music_playlist_related().await;
|
||||||
music_album(&testfiles).await;
|
music_album().await;
|
||||||
music_search(&testfiles).await;
|
music_search().await;
|
||||||
music_search_tracks(&testfiles).await;
|
music_search_tracks().await;
|
||||||
music_search_albums(&testfiles).await;
|
music_search_albums().await;
|
||||||
music_search_artists(&testfiles).await;
|
music_search_artists().await;
|
||||||
music_search_playlists(&testfiles).await;
|
music_search_playlists().await;
|
||||||
music_search_cont(&testfiles).await;
|
music_search_cont().await;
|
||||||
music_search_suggestion(&testfiles).await;
|
music_search_suggestion().await;
|
||||||
music_artist(&testfiles).await;
|
music_artist().await;
|
||||||
music_details(&testfiles).await;
|
music_details().await;
|
||||||
music_lyrics(&testfiles).await;
|
music_lyrics().await;
|
||||||
music_related(&testfiles).await;
|
music_related().await;
|
||||||
music_radio(&testfiles).await;
|
music_radio().await;
|
||||||
music_radio_cont(&testfiles).await;
|
music_radio_cont().await;
|
||||||
music_new_albums(&testfiles).await;
|
music_new_albums().await;
|
||||||
music_new_videos(&testfiles).await;
|
music_new_videos().await;
|
||||||
music_charts(&testfiles).await;
|
music_charts().await;
|
||||||
music_genres(&testfiles).await;
|
music_genres().await;
|
||||||
music_genre(&testfiles).await;
|
music_genre().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CLIENT_TYPES: [ClientType; 5] = [
|
const CLIENT_TYPES: [ClientType; 5] = [
|
||||||
|
|
@ -136,14 +136,12 @@ fn rp_testfile(json_path: &Path) -> RustyPipe {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn player(testfiles: &Path) {
|
async fn player() {
|
||||||
let video_id = "pPvd8UxmSbQ";
|
let video_id = "pPvd8UxmSbQ";
|
||||||
|
|
||||||
for client_type in CLIENT_TYPES {
|
for client_type in CLIENT_TYPES {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path =
|
||||||
json_path.push("player");
|
path!(*TESTFILES_DIR / "player" / format!("{client_type:?}_video.json").to_lowercase());
|
||||||
json_path.push(format!("{client_type:?}_video.json").to_lowercase());
|
|
||||||
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -156,14 +154,12 @@ async fn player(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn player_model(testfiles: &Path) {
|
async fn player_model() {
|
||||||
let rp = RustyPipe::builder().strict().build();
|
let rp = RustyPipe::builder().strict().build();
|
||||||
|
|
||||||
for (name, id) in [("multilanguage", "tVWWp1PqDus"), ("hdr", "LXb3EKWsInQ")] {
|
for (name, id) in [("multilanguage", "tVWWp1PqDus"), ("hdr", "LXb3EKWsInQ")] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path =
|
||||||
json_path.push("player_model");
|
path!(*TESTFILES_DIR / "player_model" / format!("{name}.json").to_lowercase());
|
||||||
json_path.push(format!("{name}.json").to_lowercase());
|
|
||||||
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -180,15 +176,13 @@ async fn player_model(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn playlist(testfiles: &Path) {
|
async fn playlist() {
|
||||||
for (name, id) in [
|
for (name, id) in [
|
||||||
("short", "RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk"),
|
("short", "RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk"),
|
||||||
("long", "PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ"),
|
("long", "PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ"),
|
||||||
("nomusic", "PL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe"),
|
("nomusic", "PL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe"),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "playlist" / format!("playlist_{name}.json"));
|
||||||
json_path.push("playlist");
|
|
||||||
json_path.push(format!("playlist_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -198,10 +192,8 @@ async fn playlist(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn playlist_cont(testfiles: &Path) {
|
async fn playlist_cont() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "playlist" / "playlist_cont.json");
|
||||||
json_path.push("playlist");
|
|
||||||
json_path.push("playlist_cont.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +209,7 @@ async fn playlist_cont(testfiles: &Path) {
|
||||||
playlist.videos.next(rp.query()).await.unwrap().unwrap();
|
playlist.videos.next(rp.query()).await.unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn video_details(testfiles: &Path) {
|
async fn video_details() {
|
||||||
for (name, id) in [
|
for (name, id) in [
|
||||||
("music", "XuM2onMGvTI"),
|
("music", "XuM2onMGvTI"),
|
||||||
("mv", "ZeerrnuLi5E"),
|
("mv", "ZeerrnuLi5E"),
|
||||||
|
|
@ -226,9 +218,8 @@ async fn video_details(testfiles: &Path) {
|
||||||
("live", "86YLFOog4GM"),
|
("live", "86YLFOog4GM"),
|
||||||
("agegate", "HRKu0cvrr_o"),
|
("agegate", "HRKu0cvrr_o"),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path =
|
||||||
json_path.push("video_details");
|
path!(*TESTFILES_DIR / "video_details" / format!("video_details_{name}.json"));
|
||||||
json_path.push(format!("video_details_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -238,10 +229,8 @@ async fn video_details(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn comments_top(testfiles: &Path) {
|
async fn comments_top() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "video_details" / "comments_top.json");
|
||||||
json_path.push("video_details");
|
|
||||||
json_path.push("comments_top.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -258,10 +247,8 @@ async fn comments_top(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn comments_latest(testfiles: &Path) {
|
async fn comments_latest() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "video_details" / "comments_latest.json");
|
||||||
json_path.push("video_details");
|
|
||||||
json_path.push("comments_latest.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -278,10 +265,8 @@ async fn comments_latest(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn recommendations(testfiles: &Path) {
|
async fn recommendations() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "video_details" / "recommendations.json");
|
||||||
json_path.push("video_details");
|
|
||||||
json_path.push("recommendations.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +278,7 @@ async fn recommendations(testfiles: &Path) {
|
||||||
details.recommended.next(rp.query()).await.unwrap();
|
details.recommended.next(rp.query()).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn channel_videos(testfiles: &Path) {
|
async fn channel_videos() {
|
||||||
for (name, id) in [
|
for (name, id) in [
|
||||||
("base", "UC2DjFE7Xf11URZqWBigcVOQ"),
|
("base", "UC2DjFE7Xf11URZqWBigcVOQ"),
|
||||||
("music", "UC_vmjW5e1xEHhYjY2a0kK1A"), // YouTube Music channels have no videos
|
("music", "UC_vmjW5e1xEHhYjY2a0kK1A"), // YouTube Music channels have no videos
|
||||||
|
|
@ -302,9 +287,7 @@ async fn channel_videos(testfiles: &Path) {
|
||||||
("empty", "UCxBa895m48H5idw5li7h-0g"),
|
("empty", "UCxBa895m48H5idw5li7h-0g"),
|
||||||
("upcoming", "UCcvfHa-GHSOHFAjU0-Ie57A"),
|
("upcoming", "UCcvfHa-GHSOHFAjU0-Ie57A"),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "channel" / format!("channel_videos_{name}.json"));
|
||||||
json_path.push("channel");
|
|
||||||
json_path.push(format!("channel_videos_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -314,10 +297,8 @@ async fn channel_videos(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn channel_shorts(testfiles: &Path) {
|
async fn channel_shorts() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "channel" / "channel_shorts.json");
|
||||||
json_path.push("channel");
|
|
||||||
json_path.push("channel_shorts.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -329,10 +310,8 @@ async fn channel_shorts(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn channel_livestreams(testfiles: &Path) {
|
async fn channel_livestreams() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "channel" / "channel_livestreams.json");
|
||||||
json_path.push("channel");
|
|
||||||
json_path.push("channel_livestreams.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -344,10 +323,8 @@ async fn channel_livestreams(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn channel_playlists(testfiles: &Path) {
|
async fn channel_playlists() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "channel" / "channel_playlists.json");
|
||||||
json_path.push("channel");
|
|
||||||
json_path.push("channel_playlists.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -359,10 +336,8 @@ async fn channel_playlists(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn channel_info(testfiles: &Path) {
|
async fn channel_info() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "channel" / "channel_info.json");
|
||||||
json_path.push("channel");
|
|
||||||
json_path.push("channel_info.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -374,10 +349,8 @@ async fn channel_info(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn channel_videos_cont(testfiles: &Path) {
|
async fn channel_videos_cont() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "channel" / "channel_videos_cont.json");
|
||||||
json_path.push("channel");
|
|
||||||
json_path.push("channel_videos_cont.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -393,10 +366,8 @@ async fn channel_videos_cont(testfiles: &Path) {
|
||||||
videos.content.next(rp.query()).await.unwrap().unwrap();
|
videos.content.next(rp.query()).await.unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn channel_playlists_cont(testfiles: &Path) {
|
async fn channel_playlists_cont() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "channel" / "channel_playlists_cont.json");
|
||||||
json_path.push("channel");
|
|
||||||
json_path.push("channel_playlists_cont.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -412,10 +383,8 @@ async fn channel_playlists_cont(testfiles: &Path) {
|
||||||
playlists.content.next(rp.query()).await.unwrap().unwrap();
|
playlists.content.next(rp.query()).await.unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn search(testfiles: &Path) {
|
async fn search() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "search" / "default.json");
|
||||||
json_path.push("search");
|
|
||||||
json_path.push("default.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -424,10 +393,8 @@ async fn search(testfiles: &Path) {
|
||||||
rp.query().search("doobydoobap").await.unwrap();
|
rp.query().search("doobydoobap").await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn search_cont(testfiles: &Path) {
|
async fn search_cont() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "search" / "cont.json");
|
||||||
json_path.push("search");
|
|
||||||
json_path.push("cont.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -439,10 +406,8 @@ async fn search_cont(testfiles: &Path) {
|
||||||
search.items.next(rp.query()).await.unwrap().unwrap();
|
search.items.next(rp.query()).await.unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn search_playlists(testfiles: &Path) {
|
async fn search_playlists() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "search" / "playlists.json");
|
||||||
json_path.push("search");
|
|
||||||
json_path.push("playlists.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -454,10 +419,8 @@ async fn search_playlists(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn search_empty(testfiles: &Path) {
|
async fn search_empty() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "search" / "empty.json");
|
||||||
json_path.push("search");
|
|
||||||
json_path.push("empty.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -474,10 +437,8 @@ async fn search_empty(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn startpage(testfiles: &Path) {
|
async fn startpage() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "trends" / "startpage.json");
|
||||||
json_path.push("trends");
|
|
||||||
json_path.push("startpage.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -486,10 +447,8 @@ async fn startpage(testfiles: &Path) {
|
||||||
rp.query().startpage().await.unwrap();
|
rp.query().startpage().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn startpage_cont(testfiles: &Path) {
|
async fn startpage_cont() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "trends" / "startpage_cont.json");
|
||||||
json_path.push("trends");
|
|
||||||
json_path.push("startpage_cont.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -501,10 +460,8 @@ async fn startpage_cont(testfiles: &Path) {
|
||||||
startpage.next(rp.query()).await.unwrap();
|
startpage.next(rp.query()).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn trending(testfiles: &Path) {
|
async fn trending() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "trends" / "trending_videos.json");
|
||||||
json_path.push("trends");
|
|
||||||
json_path.push("trending.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -513,15 +470,13 @@ async fn trending(testfiles: &Path) {
|
||||||
rp.query().trending().await.unwrap();
|
rp.query().trending().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_playlist(testfiles: &Path) {
|
async fn music_playlist() {
|
||||||
for (name, id) in [
|
for (name, id) in [
|
||||||
("short", "RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk"),
|
("short", "RDCLAK5uy_kFQXdnqMaQCVx2wpUM4ZfbsGCDibZtkJk"),
|
||||||
("long", "PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ"),
|
("long", "PL5dDx681T4bR7ZF1IuWzOv1omlRbE7PiJ"),
|
||||||
("nomusic", "PL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe"),
|
("nomusic", "PL1J-6JOckZtE_P9Xx8D3b2O6w0idhuKBe"),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_playlist" / format!("playlist_{name}.json"));
|
||||||
json_path.push("music_playlist");
|
|
||||||
json_path.push(format!("playlist_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -531,10 +486,8 @@ async fn music_playlist(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_playlist_cont(testfiles: &Path) {
|
async fn music_playlist_cont() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_playlist" / "playlist_cont.json");
|
||||||
json_path.push("music_playlist");
|
|
||||||
json_path.push("playlist_cont.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -550,10 +503,8 @@ async fn music_playlist_cont(testfiles: &Path) {
|
||||||
playlist.tracks.next(rp.query()).await.unwrap().unwrap();
|
playlist.tracks.next(rp.query()).await.unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_playlist_related(testfiles: &Path) {
|
async fn music_playlist_related() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_playlist" / "playlist_related.json");
|
||||||
json_path.push("music_playlist");
|
|
||||||
json_path.push("playlist_related.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -574,7 +525,7 @@ async fn music_playlist_related(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_album(testfiles: &Path) {
|
async fn music_album() {
|
||||||
for (name, id) in [
|
for (name, id) in [
|
||||||
("one_artist", "MPREb_nlBWQROfvjo"),
|
("one_artist", "MPREb_nlBWQROfvjo"),
|
||||||
("various_artists", "MPREb_8QkDeEIawvX"),
|
("various_artists", "MPREb_8QkDeEIawvX"),
|
||||||
|
|
@ -582,9 +533,7 @@ async fn music_album(testfiles: &Path) {
|
||||||
("description", "MPREb_PiyfuVl6aYd"),
|
("description", "MPREb_PiyfuVl6aYd"),
|
||||||
("unavailable", "MPREb_AzuWg8qAVVl"),
|
("unavailable", "MPREb_AzuWg8qAVVl"),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_playlist" / format!("album_{name}.json"));
|
||||||
json_path.push("music_playlist");
|
|
||||||
json_path.push(format!("album_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -594,16 +543,14 @@ async fn music_album(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_search(testfiles: &Path) {
|
async fn music_search() {
|
||||||
for (name, query) in [
|
for (name, query) in [
|
||||||
("default", "black mamba"),
|
("default", "black mamba"),
|
||||||
("typo", "liblingsmensch"),
|
("typo", "liblingsmensch"),
|
||||||
("radio", "pop radio"),
|
("radio", "pop radio"),
|
||||||
("artist", "taylor swift"),
|
("artist", "taylor swift"),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_search" / format!("main_{name}.json"));
|
||||||
json_path.push("music_search");
|
|
||||||
json_path.push(format!("main_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -613,7 +560,7 @@ async fn music_search(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_search_tracks(testfiles: &Path) {
|
async fn music_search_tracks() {
|
||||||
for (name, query, videos) in [
|
for (name, query, videos) in [
|
||||||
("default", "black mamba", false),
|
("default", "black mamba", false),
|
||||||
("videos", "black mamba", true),
|
("videos", "black mamba", true),
|
||||||
|
|
@ -624,9 +571,7 @@ async fn music_search_tracks(testfiles: &Path) {
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_search" / format!("tracks_{name}.json"));
|
||||||
json_path.push("music_search");
|
|
||||||
json_path.push(format!("tracks_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -640,10 +585,8 @@ async fn music_search_tracks(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_search_albums(testfiles: &Path) {
|
async fn music_search_albums() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_search" / "albums.json");
|
||||||
json_path.push("music_search");
|
|
||||||
json_path.push("albums.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -652,10 +595,8 @@ async fn music_search_albums(testfiles: &Path) {
|
||||||
rp.query().music_search_albums("black mamba").await.unwrap();
|
rp.query().music_search_albums("black mamba").await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_search_artists(testfiles: &Path) {
|
async fn music_search_artists() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_search" / "artists.json");
|
||||||
json_path.push("music_search");
|
|
||||||
json_path.push("artists.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -667,11 +608,9 @@ async fn music_search_artists(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_search_playlists(testfiles: &Path) {
|
async fn music_search_playlists() {
|
||||||
for (name, community) in [("ytm", false), ("community", true)] {
|
for (name, community) in [("ytm", false), ("community", true)] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_search" / format!("playlists_{name}.json"));
|
||||||
json_path.push("music_search");
|
|
||||||
json_path.push(format!("playlists_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -684,10 +623,8 @@ async fn music_search_playlists(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_search_cont(testfiles: &Path) {
|
async fn music_search_cont() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_search" / "tracks_cont.json");
|
||||||
json_path.push("music_search");
|
|
||||||
json_path.push("tracks_cont.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -699,11 +636,9 @@ async fn music_search_cont(testfiles: &Path) {
|
||||||
res.items.next(rp.query()).await.unwrap().unwrap();
|
res.items.next(rp.query()).await.unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_search_suggestion(testfiles: &Path) {
|
async fn music_search_suggestion() {
|
||||||
for (name, query) in [("default", "t"), ("empty", "reujbhevmfndxnjrze")] {
|
for (name, query) in [("default", "t"), ("empty", "reujbhevmfndxnjrze")] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_search" / format!("suggestion_{name}.json"));
|
||||||
json_path.push("music_search");
|
|
||||||
json_path.push(format!("suggestion_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -713,7 +648,7 @@ async fn music_search_suggestion(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_artist(testfiles: &Path) {
|
async fn music_artist() {
|
||||||
for (name, id, all_albums) in [
|
for (name, id, all_albums) in [
|
||||||
("default", "UClmXPfaYhXOYsNn_QUyheWQ", true),
|
("default", "UClmXPfaYhXOYsNn_QUyheWQ", true),
|
||||||
("no_more_albums", "UC_vmjW5e1xEHhYjY2a0kK1A", true),
|
("no_more_albums", "UC_vmjW5e1xEHhYjY2a0kK1A", true),
|
||||||
|
|
@ -722,9 +657,7 @@ async fn music_artist(testfiles: &Path) {
|
||||||
("only_more_singles", "UC0aXrjVxG5pZr99v77wZdPQ", true),
|
("only_more_singles", "UC0aXrjVxG5pZr99v77wZdPQ", true),
|
||||||
("secondary_channel", "UCC9192yGQD25eBZgFZ84MPw", false),
|
("secondary_channel", "UCC9192yGQD25eBZgFZ84MPw", false),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_artist" / format!("artist_{name}.json"));
|
||||||
json_path.push("music_artist");
|
|
||||||
json_path.push(format!("artist_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -734,11 +667,9 @@ async fn music_artist(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_details(testfiles: &Path) {
|
async fn music_details() {
|
||||||
for (name, id) in [("mv", "ZeerrnuLi5E"), ("track", "7nigXQS1Xb0")] {
|
for (name, id) in [("mv", "ZeerrnuLi5E"), ("track", "7nigXQS1Xb0")] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_details" / format!("details_{name}.json"));
|
||||||
json_path.push("music_details");
|
|
||||||
json_path.push(format!("details_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -748,10 +679,8 @@ async fn music_details(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_lyrics(testfiles: &Path) {
|
async fn music_lyrics() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_details" / "lyrics.json");
|
||||||
json_path.push("music_details");
|
|
||||||
json_path.push("lyrics.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -766,10 +695,8 @@ async fn music_lyrics(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_related(testfiles: &Path) {
|
async fn music_related() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_details" / "related.json");
|
||||||
json_path.push("music_details");
|
|
||||||
json_path.push("related.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -784,11 +711,9 @@ async fn music_related(testfiles: &Path) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_radio(testfiles: &Path) {
|
async fn music_radio() {
|
||||||
for (name, id) in [("mv", "RDAMVMZeerrnuLi5E"), ("track", "RDAMVM7nigXQS1Xb0")] {
|
for (name, id) in [("mv", "RDAMVMZeerrnuLi5E"), ("track", "RDAMVM7nigXQS1Xb0")] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_details" / format!("radio_{name}.json"));
|
||||||
json_path.push("music_details");
|
|
||||||
json_path.push(format!("radio_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -798,10 +723,8 @@ async fn music_radio(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_radio_cont(testfiles: &Path) {
|
async fn music_radio_cont() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_details" / "radio_cont.json");
|
||||||
json_path.push("music_details");
|
|
||||||
json_path.push("radio_cont.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -813,10 +736,8 @@ async fn music_radio_cont(testfiles: &Path) {
|
||||||
res.next(rp.query()).await.unwrap().unwrap();
|
res.next(rp.query()).await.unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_new_albums(testfiles: &Path) {
|
async fn music_new_albums() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_new" / "albums_default.json");
|
||||||
json_path.push("music_new");
|
|
||||||
json_path.push("albums_default.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -825,10 +746,8 @@ async fn music_new_albums(testfiles: &Path) {
|
||||||
rp.query().music_new_albums().await.unwrap();
|
rp.query().music_new_albums().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_new_videos(testfiles: &Path) {
|
async fn music_new_videos() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_new" / "videos_default.json");
|
||||||
json_path.push("music_new");
|
|
||||||
json_path.push("videos_default.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -837,11 +756,9 @@ async fn music_new_videos(testfiles: &Path) {
|
||||||
rp.query().music_new_videos().await.unwrap();
|
rp.query().music_new_videos().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_charts(testfiles: &Path) {
|
async fn music_charts() {
|
||||||
for (name, country) in [("global", Some(Country::Zz)), ("US", Some(Country::Us))] {
|
for (name, country) in [("global", Some(Country::Zz)), ("US", Some(Country::Us))] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_charts" / format!("charts_{name}.json"));
|
||||||
json_path.push("music_charts");
|
|
||||||
json_path.push(&format!("charts_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -851,10 +768,8 @@ async fn music_charts(testfiles: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_genres(testfiles: &Path) {
|
async fn music_genres() {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_genres" / "genres.json");
|
||||||
json_path.push("music_genres");
|
|
||||||
json_path.push("genres.json");
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -863,14 +778,12 @@ async fn music_genres(testfiles: &Path) {
|
||||||
rp.query().music_genres().await.unwrap();
|
rp.query().music_genres().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn music_genre(testfiles: &Path) {
|
async fn music_genre() {
|
||||||
for (name, id) in [
|
for (name, id) in [
|
||||||
("default", "ggMPOg1uX1lMbVZmbzl6NlJ3"),
|
("default", "ggMPOg1uX1lMbVZmbzl6NlJ3"),
|
||||||
("mood", "ggMPOg1uX1JOQWZFeDByc2Jm"),
|
("mood", "ggMPOg1uX1JOQWZFeDByc2Jm"),
|
||||||
] {
|
] {
|
||||||
let mut json_path = testfiles.to_path_buf();
|
let json_path = path!(*TESTFILES_DIR / "music_genres" / format!("genre_{name}.json"));
|
||||||
json_path.push("music_genres");
|
|
||||||
json_path.push(&format!("genre_{name}.json"));
|
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use path_macro::path;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustypipe::timeago::TimeUnit;
|
|
||||||
|
|
||||||
use crate::util;
|
use crate::{
|
||||||
|
model::TimeUnit,
|
||||||
const TARGET_PATH: &str = "src/util/dictionary.rs";
|
util::{self, SRC_DIR},
|
||||||
|
};
|
||||||
|
|
||||||
fn parse_tu(tu: &str) -> (u8, Option<TimeUnit>) {
|
fn parse_tu(tu: &str) -> (u8, Option<TimeUnit>) {
|
||||||
static TU_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\d*)(\w?)$").unwrap());
|
static TU_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\d*)(\w?)$").unwrap());
|
||||||
|
|
@ -30,8 +30,8 @@ fn parse_tu(tu: &str) -> (u8, Option<TimeUnit>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_dictionary(project_root: &Path) {
|
pub fn generate_dictionary() {
|
||||||
let dict = util::read_dict(project_root);
|
let dict = util::read_dict();
|
||||||
|
|
||||||
let code_head = r#"// This file is automatically generated. DO NOT EDIT.
|
let code_head = r#"// This file is automatically generated. DO NOT EDIT.
|
||||||
// See codegen/gen_dictionary.rs for the generation code.
|
// See codegen/gen_dictionary.rs for the generation code.
|
||||||
|
|
@ -169,7 +169,6 @@ pub(crate) fn entry(lang: Language) -> Entry {
|
||||||
|
|
||||||
let code = format!("{code_head}\n{code_timeago_tokens}");
|
let code = format!("{code_head}\n{code_timeago_tokens}");
|
||||||
|
|
||||||
let mut target_path = project_root.to_path_buf();
|
let target_path = path!(*SRC_DIR / "util" / "dictionary.rs");
|
||||||
target_path.push(TARGET_PATH);
|
|
||||||
std::fs::write(target_path, code).unwrap();
|
std::fs::write(target_path, code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
|
use path_macro::path;
|
||||||
use reqwest::header;
|
use reqwest::header;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_with::serde_as;
|
use serde_with::serde_as;
|
||||||
use serde_with::VecSkipError;
|
use serde_with::VecSkipError;
|
||||||
|
|
||||||
use crate::util::Text;
|
use crate::model::Text;
|
||||||
|
use crate::util::SRC_DIR;
|
||||||
|
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
|
@ -137,7 +138,7 @@ struct LanguageCountryCommand {
|
||||||
hl: String,
|
hl: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn generate_locales(project_root: &Path) {
|
pub async fn generate_locales() {
|
||||||
let (languages, countries) = get_locales().await;
|
let (languages, countries) = get_locales().await;
|
||||||
|
|
||||||
let code_head = r#"// This file is automatically generated. DO NOT EDIT.
|
let code_head = r#"// This file is automatically generated. DO NOT EDIT.
|
||||||
|
|
@ -288,8 +289,7 @@ pub enum Country {
|
||||||
"{code_head}\n{code_langs}\n{code_countries}\n{code_lang_array}\n{code_country_array}\n{code_lang_names}\n{code_country_names}\n{code_foot}"
|
"{code_head}\n{code_langs}\n{code_countries}\n{code_lang_array}\n{code_country_array}\n{code_lang_names}\n{code_country_names}\n{code_foot}"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut target_path = project_root.to_path_buf();
|
let target_path = path!(*SRC_DIR / "param" / "locale.rs");
|
||||||
target_path.push("src/param/locale.rs");
|
|
||||||
std::fs::write(target_path, code).unwrap();
|
std::fs::write(target_path, code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,19 @@ mod abtest;
|
||||||
mod collect_album_types;
|
mod collect_album_types;
|
||||||
mod collect_large_numbers;
|
mod collect_large_numbers;
|
||||||
mod collect_playlist_dates;
|
mod collect_playlist_dates;
|
||||||
|
mod collect_video_durations;
|
||||||
mod download_testfiles;
|
mod download_testfiles;
|
||||||
mod gen_dictionary;
|
mod gen_dictionary;
|
||||||
mod gen_locales;
|
mod gen_locales;
|
||||||
|
mod model;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
command: Commands,
|
command: Commands,
|
||||||
#[clap(short = 'd', default_value = "..")]
|
|
||||||
project_root: PathBuf,
|
|
||||||
#[clap(short, default_value = "8")]
|
#[clap(short, default_value = "8")]
|
||||||
concurrency: usize,
|
concurrency: usize,
|
||||||
}
|
}
|
||||||
|
|
@ -26,6 +24,7 @@ enum Commands {
|
||||||
CollectPlaylistDates,
|
CollectPlaylistDates,
|
||||||
CollectLargeNumbers,
|
CollectLargeNumbers,
|
||||||
CollectAlbumTypes,
|
CollectAlbumTypes,
|
||||||
|
CollectVideoDurations,
|
||||||
ParsePlaylistDates,
|
ParsePlaylistDates,
|
||||||
ParseLargeNumbers,
|
ParseLargeNumbers,
|
||||||
ParseAlbumTypes,
|
ParseAlbumTypes,
|
||||||
|
|
@ -47,28 +46,25 @@ async fn main() {
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Commands::CollectPlaylistDates => {
|
Commands::CollectPlaylistDates => {
|
||||||
collect_playlist_dates::collect_dates(&cli.project_root, cli.concurrency).await;
|
collect_playlist_dates::collect_dates(cli.concurrency).await;
|
||||||
}
|
}
|
||||||
Commands::CollectLargeNumbers => {
|
Commands::CollectLargeNumbers => {
|
||||||
collect_large_numbers::collect_large_numbers(&cli.project_root, cli.concurrency).await;
|
collect_large_numbers::collect_large_numbers(cli.concurrency).await;
|
||||||
}
|
}
|
||||||
Commands::CollectAlbumTypes => {
|
Commands::CollectAlbumTypes => {
|
||||||
collect_album_types::collect_album_types(&cli.project_root, cli.concurrency).await;
|
collect_album_types::collect_album_types(cli.concurrency).await;
|
||||||
}
|
}
|
||||||
Commands::ParsePlaylistDates => {
|
Commands::CollectVideoDurations => {
|
||||||
collect_playlist_dates::write_samples_to_dict(&cli.project_root)
|
collect_video_durations::collect_video_durations(cli.concurrency).await;
|
||||||
}
|
}
|
||||||
Commands::ParseLargeNumbers => {
|
Commands::ParsePlaylistDates => collect_playlist_dates::write_samples_to_dict(),
|
||||||
collect_large_numbers::write_samples_to_dict(&cli.project_root)
|
Commands::ParseLargeNumbers => collect_large_numbers::write_samples_to_dict(),
|
||||||
}
|
Commands::ParseAlbumTypes => collect_album_types::write_samples_to_dict(),
|
||||||
Commands::ParseAlbumTypes => collect_album_types::write_samples_to_dict(&cli.project_root),
|
|
||||||
Commands::GenLocales => {
|
Commands::GenLocales => {
|
||||||
gen_locales::generate_locales(&cli.project_root).await;
|
gen_locales::generate_locales().await;
|
||||||
}
|
|
||||||
Commands::GenDict => gen_dictionary::generate_dictionary(&cli.project_root),
|
|
||||||
Commands::DownloadTestfiles => {
|
|
||||||
download_testfiles::download_testfiles(&cli.project_root).await
|
|
||||||
}
|
}
|
||||||
|
Commands::GenDict => gen_dictionary::generate_dictionary(),
|
||||||
|
Commands::DownloadTestfiles => download_testfiles::download_testfiles().await,
|
||||||
Commands::AbTest { id, n } => {
|
Commands::AbTest { id, n } => {
|
||||||
match id {
|
match id {
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
|
|
|
||||||
260
codegen/src/model.rs
Normal file
260
codegen/src/model.rs
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use rustypipe::{client::YTContext, model::AlbumType, param::Language};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_with::{serde_as, DefaultOnError, VecSkipError};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct DictEntry {
|
||||||
|
/// List of languages that should be treated equally (e.g. EnUs/EnGb/EnIn)
|
||||||
|
pub equivalent: Vec<Language>,
|
||||||
|
/// Should the language be parsed by character instead of by word?
|
||||||
|
/// (e.g. Chinese/Japanese)
|
||||||
|
pub by_char: bool,
|
||||||
|
/// Tokens for parsing timeago strings.
|
||||||
|
///
|
||||||
|
/// Format: Parsed token -> \[Quantity\] Identifier
|
||||||
|
///
|
||||||
|
/// Identifiers: `Y`(ear), `M`(month), `W`(eek), `D`(ay),
|
||||||
|
/// `h`(our), `m`(inute), `s`(econd)
|
||||||
|
pub timeago_tokens: BTreeMap<String, String>,
|
||||||
|
/// Order in which to parse numeric date components. Formatted as
|
||||||
|
/// a string of date identifiers (Y, M, D).
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// - 03.01.2020 => `"DMY"`
|
||||||
|
/// - Jan 3, 2020 => `"DY"`
|
||||||
|
pub date_order: String,
|
||||||
|
/// Tokens for parsing month names.
|
||||||
|
///
|
||||||
|
/// Format: Parsed token -> Month number (starting from 1)
|
||||||
|
pub months: BTreeMap<String, u8>,
|
||||||
|
/// Tokens for parsing date strings with no digits (e.g. Today, Tomorrow)
|
||||||
|
///
|
||||||
|
/// Format: Parsed token -> \[Quantity\] Identifier
|
||||||
|
pub timeago_nd_tokens: BTreeMap<String, String>,
|
||||||
|
/// Are commas (instead of points) used as decimal separators?
|
||||||
|
pub comma_decimal: bool,
|
||||||
|
/// Tokens for parsing decimal prefixes (K, M, B, ...)
|
||||||
|
///
|
||||||
|
/// Format: Parsed token -> decimal power
|
||||||
|
pub number_tokens: BTreeMap<String, u8>,
|
||||||
|
/// Tokens for parsing number strings with no digits (e.g. "No videos")
|
||||||
|
///
|
||||||
|
/// Format: Parsed token -> value
|
||||||
|
pub number_nd_tokens: BTreeMap<String, u8>,
|
||||||
|
/// Names of album types (Album, Single, ...)
|
||||||
|
///
|
||||||
|
/// Format: Parsed text -> Album type
|
||||||
|
pub album_types: BTreeMap<String, AlbumType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parsed time unit
|
||||||
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum TimeUnit {
|
||||||
|
Second,
|
||||||
|
Minute,
|
||||||
|
Hour,
|
||||||
|
Day,
|
||||||
|
Week,
|
||||||
|
Month,
|
||||||
|
Year,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct QBrowse<'a> {
|
||||||
|
pub context: YTContext<'a>,
|
||||||
|
pub browse_id: &'a str,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub params: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct QCont<'a> {
|
||||||
|
pub context: YTContext<'a>,
|
||||||
|
pub continuation: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct TextRuns {
|
||||||
|
pub runs: Vec<Text>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct Text {
|
||||||
|
#[serde(alias = "simpleText")]
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Channel {
|
||||||
|
pub contents: Contents,
|
||||||
|
pub header: ChannelHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ChannelHeader {
|
||||||
|
pub c4_tabbed_header_renderer: HeaderRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct HeaderRenderer {
|
||||||
|
pub subscriber_count_text: Text,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Contents {
|
||||||
|
pub two_column_browse_results_renderer: TabsRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct TabsRenderer {
|
||||||
|
#[serde_as(as = "VecSkipError<_>")]
|
||||||
|
pub tabs: Vec<TabRendererWrap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct TabRendererWrap {
|
||||||
|
pub tab_renderer: TabRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct TabRenderer {
|
||||||
|
pub content: RichGridRendererWrap,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RichGridRendererWrap {
|
||||||
|
pub rich_grid_renderer: RichGridRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RichGridRenderer {
|
||||||
|
#[serde_as(as = "VecSkipError<_>")]
|
||||||
|
pub contents: Vec<RichItemRendererWrap>,
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde_as(as = "DefaultOnError")]
|
||||||
|
pub header: Option<RichGridHeader>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RichItemRendererWrap {
|
||||||
|
pub rich_item_renderer: RichItemRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RichItemRenderer {
|
||||||
|
pub content: VideoRendererWrap,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct VideoRendererWrap {
|
||||||
|
pub video_renderer: VideoRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct VideoRenderer {
|
||||||
|
/// `24,194 views`
|
||||||
|
pub view_count_text: Text,
|
||||||
|
/// `19K views`
|
||||||
|
pub short_view_count_text: Text,
|
||||||
|
pub length_text: LengthText,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct LengthText {
|
||||||
|
/// `18 minutes, 26 seconds`
|
||||||
|
pub accessibility: Accessibility,
|
||||||
|
/// `18:26`
|
||||||
|
pub simple_text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Accessibility {
|
||||||
|
pub accessibility_data: AccessibilityData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct AccessibilityData {
|
||||||
|
pub label: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RichGridHeader {
|
||||||
|
pub feed_filter_chip_bar_renderer: ChipBar,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ChipBar {
|
||||||
|
pub contents: Vec<Chip>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Chip {
|
||||||
|
pub chip_cloud_chip_renderer: ChipRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ChipRenderer {
|
||||||
|
pub navigation_endpoint: NavigationEndpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct NavigationEndpoint {
|
||||||
|
pub continuation_command: ContinuationCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ContinuationCommand {
|
||||||
|
pub token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ContinuationResponse {
|
||||||
|
pub on_response_received_actions: Vec<ContinuationAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ContinuationAction {
|
||||||
|
pub reload_continuation_items_command: ContinuationItemsWrap,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ContinuationItemsWrap {
|
||||||
|
#[serde_as(as = "VecSkipError<_>")]
|
||||||
|
pub continuation_items: Vec<RichItemRendererWrap>,
|
||||||
|
}
|
||||||
|
|
@ -1,120 +1,50 @@
|
||||||
use std::{
|
use std::{collections::BTreeMap, fs::File, io::BufReader, path::PathBuf, str::FromStr};
|
||||||
collections::BTreeMap,
|
|
||||||
fs::File,
|
|
||||||
io::BufReader,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use path_macro::path;
|
use path_macro::path;
|
||||||
use rustypipe::{client::YTContext, model::AlbumType, param::Language};
|
use regex::Regex;
|
||||||
|
use rustypipe::param::Language;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
static DICT_PATH: Lazy<PathBuf> = Lazy::new(|| path!("testfiles" / "dict" / "dictionary.json"));
|
use crate::model::DictEntry;
|
||||||
static DICT_OVERRIDE_PATH: Lazy<PathBuf> =
|
|
||||||
Lazy::new(|| path!("testfiles" / "dict" / "dictionary_override.json"));
|
/// Get the path of the `testfiles` directory
|
||||||
|
pub static TESTFILES_DIR: Lazy<PathBuf> = Lazy::new(|| {
|
||||||
|
path!(env!("CARGO_MANIFEST_DIR") / ".." / "testfiles")
|
||||||
|
.canonicalize()
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
/// Get the path of the `dict` directory
|
||||||
|
pub static DICT_DIR: Lazy<PathBuf> = Lazy::new(|| path!(*TESTFILES_DIR / "dict"));
|
||||||
|
/// Get the path of the `src` directory
|
||||||
|
pub static SRC_DIR: Lazy<PathBuf> = Lazy::new(|| path!(env!("CARGO_MANIFEST_DIR") / ".." / "src"));
|
||||||
|
|
||||||
type Dictionary = BTreeMap<Language, DictEntry>;
|
type Dictionary = BTreeMap<Language, DictEntry>;
|
||||||
type DictionaryOverride = BTreeMap<Language, DictOverrideEntry>;
|
type DictionaryOverride = BTreeMap<Language, DictOverrideEntry>;
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct DictEntry {
|
struct DictOverrideEntry {
|
||||||
/// List of languages that should be treated equally (e.g. EnUs/EnGb/EnIn)
|
number_tokens: BTreeMap<String, Option<u8>>,
|
||||||
pub equivalent: Vec<Language>,
|
number_nd_tokens: BTreeMap<String, Option<u8>>,
|
||||||
/// Should the language be parsed by character instead of by word?
|
|
||||||
/// (e.g. Chinese/Japanese)
|
|
||||||
pub by_char: bool,
|
|
||||||
/// Tokens for parsing timeago strings.
|
|
||||||
///
|
|
||||||
/// Format: Parsed token -> \[Quantity\] Identifier
|
|
||||||
///
|
|
||||||
/// Identifiers: `Y`(ear), `M`(month), `W`(eek), `D`(ay),
|
|
||||||
/// `h`(our), `m`(inute), `s`(econd)
|
|
||||||
pub timeago_tokens: BTreeMap<String, String>,
|
|
||||||
/// Order in which to parse numeric date components. Formatted as
|
|
||||||
/// a string of date identifiers (Y, M, D).
|
|
||||||
///
|
|
||||||
/// Examples:
|
|
||||||
///
|
|
||||||
/// - 03.01.2020 => `"DMY"`
|
|
||||||
/// - Jan 3, 2020 => `"DY"`
|
|
||||||
pub date_order: String,
|
|
||||||
/// Tokens for parsing month names.
|
|
||||||
///
|
|
||||||
/// Format: Parsed token -> Month number (starting from 1)
|
|
||||||
pub months: BTreeMap<String, u8>,
|
|
||||||
/// Tokens for parsing date strings with no digits (e.g. Today, Tomorrow)
|
|
||||||
///
|
|
||||||
/// Format: Parsed token -> \[Quantity\] Identifier
|
|
||||||
pub timeago_nd_tokens: BTreeMap<String, String>,
|
|
||||||
/// Are commas (instead of points) used as decimal separators?
|
|
||||||
pub comma_decimal: bool,
|
|
||||||
/// Tokens for parsing decimal prefixes (K, M, B, ...)
|
|
||||||
///
|
|
||||||
/// Format: Parsed token -> decimal power
|
|
||||||
pub number_tokens: BTreeMap<String, u8>,
|
|
||||||
/// Tokens for parsing number strings with no digits (e.g. "No videos")
|
|
||||||
///
|
|
||||||
/// Format: Parsed token -> value
|
|
||||||
pub number_nd_tokens: BTreeMap<String, u8>,
|
|
||||||
/// Names of album types (Album, Single, ...)
|
|
||||||
///
|
|
||||||
/// Format: Parsed text -> Album type
|
|
||||||
pub album_types: BTreeMap<String, AlbumType>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
pub fn read_dict() -> Dictionary {
|
||||||
#[serde(default)]
|
let json_path = path!(*DICT_DIR / "dictionary.json");
|
||||||
pub struct DictOverrideEntry {
|
|
||||||
pub number_tokens: BTreeMap<String, Option<u8>>,
|
|
||||||
pub number_nd_tokens: BTreeMap<String, Option<u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct QBrowse<'a> {
|
|
||||||
pub context: YTContext<'a>,
|
|
||||||
pub browse_id: &'a str,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub params: Option<&'a str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct QCont<'a> {
|
|
||||||
pub context: YTContext<'a>,
|
|
||||||
pub continuation: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
pub struct TextRuns {
|
|
||||||
pub runs: Vec<Text>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
pub struct Text {
|
|
||||||
#[serde(alias = "simpleText")]
|
|
||||||
pub text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_dict(project_root: &Path) -> Dictionary {
|
|
||||||
let json_path = path!(project_root / *DICT_PATH);
|
|
||||||
let json_file = File::open(json_path).unwrap();
|
let json_file = File::open(json_path).unwrap();
|
||||||
serde_json::from_reader(BufReader::new(json_file)).unwrap()
|
serde_json::from_reader(BufReader::new(json_file)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_dict_override(project_root: &Path) -> DictionaryOverride {
|
fn read_dict_override() -> DictionaryOverride {
|
||||||
let json_path = path!(project_root / *DICT_OVERRIDE_PATH);
|
let json_path = path!(*DICT_DIR / "dictionary_override.json");
|
||||||
let json_file = File::open(json_path).unwrap();
|
let json_file = File::open(json_path).unwrap();
|
||||||
serde_json::from_reader(BufReader::new(json_file)).unwrap()
|
serde_json::from_reader(BufReader::new(json_file)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_dict(project_root: &Path, dict: Dictionary) {
|
pub fn write_dict(dict: Dictionary) {
|
||||||
let dict_override = read_dict_override(project_root);
|
let dict_override = read_dict_override();
|
||||||
|
|
||||||
let json_path = path!(project_root / *DICT_PATH);
|
let json_path = path!(*DICT_DIR / "dictionary.json");
|
||||||
let json_file = File::create(json_path).unwrap();
|
let json_file = File::create(json_path).unwrap();
|
||||||
|
|
||||||
fn apply_map<K: Clone + Ord, V: Clone>(map: &mut BTreeMap<K, V>, or: &BTreeMap<K, Option<V>>) {
|
fn apply_map<K: Clone + Ord, V: Clone>(map: &mut BTreeMap<K, V>, or: &BTreeMap<K, Option<V>>) {
|
||||||
|
|
@ -251,3 +181,26 @@ pub fn parse_largenum_en(string: &str) -> Option<u64> {
|
||||||
|
|
||||||
num.checked_mul((10_u64).checked_pow(exp.try_into().ok()?)?)
|
num.checked_mul((10_u64).checked_pow(exp.try_into().ok()?)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse textual video length (e.g. `0:49`, `2:02` or `1:48:18`)
|
||||||
|
/// and return the duration in seconds.
|
||||||
|
pub fn parse_video_length(text: &str) -> Option<u32> {
|
||||||
|
static VIDEO_LENGTH_REGEX: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r#"(?:(\d+)[:.])?(\d{1,2})[:.](\d{2})"#).unwrap());
|
||||||
|
VIDEO_LENGTH_REGEX.captures(text).map(|cap| {
|
||||||
|
let hrs = cap
|
||||||
|
.get(1)
|
||||||
|
.and_then(|x| x.as_str().parse::<u32>().ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let min = cap
|
||||||
|
.get(2)
|
||||||
|
.and_then(|x| x.as_str().parse::<u32>().ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let sec = cap
|
||||||
|
.get(3)
|
||||||
|
.and_then(|x| x.as_str().parse::<u32>().ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
hrs * 3600 + min * 60 + sec
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,902 +0,0 @@
|
||||||
{
|
|
||||||
"supplemental": {
|
|
||||||
"version": {
|
|
||||||
"_unicodeVersion": "13.0.0",
|
|
||||||
"_cldrVersion": "37"
|
|
||||||
},
|
|
||||||
"plurals-type-cardinal": {
|
|
||||||
"af": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ak": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"am": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"an": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ar": {
|
|
||||||
"pluralRule-count-zero": "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000",
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-few": "n % 100 = 3..10 @integer 3~10, 103~110, 1003, … @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 103.0, 1003.0, …",
|
|
||||||
"pluralRule-count-many": "n % 100 = 11..99 @integer 11~26, 111, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 100~102, 200~202, 300~302, 400~402, 500~502, 600, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ars": {
|
|
||||||
"pluralRule-count-zero": "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000",
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-few": "n % 100 = 3..10 @integer 3~10, 103~110, 1003, … @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 103.0, 1003.0, …",
|
|
||||||
"pluralRule-count-many": "n % 100 = 11..99 @integer 11~26, 111, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 100~102, 200~202, 300~302, 400~402, 500~502, 600, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"as": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"asa": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ast": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"az": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"be": {
|
|
||||||
"pluralRule-count-one": "n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, …",
|
|
||||||
"pluralRule-count-few": "n % 10 = 2..4 and n % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 2.0, 3.0, 4.0, 22.0, 23.0, 24.0, 32.0, 33.0, 102.0, 1002.0, …",
|
|
||||||
"pluralRule-count-many": "n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, …"
|
|
||||||
},
|
|
||||||
"bem": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"bez": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"bg": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"bho": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"bm": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"bn": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"bo": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"br": {
|
|
||||||
"pluralRule-count-one": "n % 10 = 1 and n % 100 != 11,71,91 @integer 1, 21, 31, 41, 51, 61, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 81.0, 101.0, 1001.0, …",
|
|
||||||
"pluralRule-count-two": "n % 10 = 2 and n % 100 != 12,72,92 @integer 2, 22, 32, 42, 52, 62, 82, 102, 1002, … @decimal 2.0, 22.0, 32.0, 42.0, 52.0, 62.0, 82.0, 102.0, 1002.0, …",
|
|
||||||
"pluralRule-count-few": "n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99 @integer 3, 4, 9, 23, 24, 29, 33, 34, 39, 43, 44, 49, 103, 1003, … @decimal 3.0, 4.0, 9.0, 23.0, 24.0, 29.0, 33.0, 34.0, 103.0, 1003.0, …",
|
|
||||||
"pluralRule-count-many": "n != 0 and n % 1000000 = 0 @integer 1000000, … @decimal 1000000.0, 1000000.00, 1000000.000, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~8, 10~20, 100, 1000, 10000, 100000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, …"
|
|
||||||
},
|
|
||||||
"brx": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"bs": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.2~0.4, 1.2~1.4, 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100.2, 1000.2, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ca": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ce": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ceb": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9 @integer 0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.3, 0.5, 0.7, 0.8, 1.0~1.3, 1.5, 1.7, 1.8, 2.0, 2.1, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, … @decimal 0.4, 0.6, 0.9, 1.4, 1.6, 1.9, 2.4, 2.6, 10.4, 100.4, 1000.4, …"
|
|
||||||
},
|
|
||||||
"cgg": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"chr": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ckb": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"cs": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-few": "i = 2..4 and v = 0 @integer 2~4",
|
|
||||||
"pluralRule-count-many": "v != 0 @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …"
|
|
||||||
},
|
|
||||||
"cy": {
|
|
||||||
"pluralRule-count-zero": "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000",
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-few": "n = 3 @integer 3 @decimal 3.0, 3.00, 3.000, 3.0000",
|
|
||||||
"pluralRule-count-many": "n = 6 @integer 6 @decimal 6.0, 6.00, 6.000, 6.0000",
|
|
||||||
"pluralRule-count-other": " @integer 4, 5, 7~20, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"da": {
|
|
||||||
"pluralRule-count-one": "n = 1 or t != 0 and i = 0,1 @integer 1 @decimal 0.1~1.6",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0~3.4, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"de": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"dsb": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 100 = 1 or f % 100 = 1 @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-two": "v = 0 and i % 100 = 2 or f % 100 = 2 @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, … @decimal 0.2, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2, 10.2, 100.2, 1000.2, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 100 = 3..4 or f % 100 = 3..4 @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, … @decimal 0.3, 0.4, 1.3, 1.4, 2.3, 2.4, 3.3, 3.4, 4.3, 4.4, 5.3, 5.4, 6.3, 6.4, 7.3, 7.4, 10.3, 100.3, 1000.3, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"dv": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"dz": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ee": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"el": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"en": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"eo": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"es": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"et": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"eu": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"fa": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ff": {
|
|
||||||
"pluralRule-count-one": "i = 0,1 @integer 0, 1 @decimal 0.0~1.5",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"fi": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"fil": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9 @integer 0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.3, 0.5, 0.7, 0.8, 1.0~1.3, 1.5, 1.7, 1.8, 2.0, 2.1, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, … @decimal 0.4, 0.6, 0.9, 1.4, 1.6, 1.9, 2.4, 2.6, 10.4, 100.4, 1000.4, …"
|
|
||||||
},
|
|
||||||
"fo": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"fr": {
|
|
||||||
"pluralRule-count-one": "i = 0,1 @integer 0, 1 @decimal 0.0~1.5",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"fur": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"fy": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ga": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-few": "n = 3..6 @integer 3~6 @decimal 3.0, 4.0, 5.0, 6.0, 3.00, 4.00, 5.00, 6.00, 3.000, 4.000, 5.000, 6.000, 3.0000, 4.0000, 5.0000, 6.0000",
|
|
||||||
"pluralRule-count-many": "n = 7..10 @integer 7~10 @decimal 7.0, 8.0, 9.0, 10.0, 7.00, 8.00, 9.00, 10.00, 7.000, 8.000, 9.000, 10.000, 7.0000, 8.0000, 9.0000, 10.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"gd": {
|
|
||||||
"pluralRule-count-one": "n = 1,11 @integer 1, 11 @decimal 1.0, 11.0, 1.00, 11.00, 1.000, 11.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2,12 @integer 2, 12 @decimal 2.0, 12.0, 2.00, 12.00, 2.000, 12.000, 2.0000",
|
|
||||||
"pluralRule-count-few": "n = 3..10,13..19 @integer 3~10, 13~19 @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 3.00",
|
|
||||||
"pluralRule-count-other": " @integer 0, 20~34, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"gl": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"gsw": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"gu": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"guw": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"gv": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 10 = 1 @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, …",
|
|
||||||
"pluralRule-count-two": "v = 0 and i % 10 = 2 @integer 2, 12, 22, 32, 42, 52, 62, 72, 102, 1002, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 100 = 0,20,40,60,80 @integer 0, 20, 40, 60, 80, 100, 120, 140, 1000, 10000, 100000, 1000000, …",
|
|
||||||
"pluralRule-count-many": "v != 0 @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 3~10, 13~19, 23, 103, 1003, …"
|
|
||||||
},
|
|
||||||
"ha": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"haw": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"he": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-two": "i = 2 and v = 0 @integer 2",
|
|
||||||
"pluralRule-count-many": "v = 0 and n != 0..10 and n % 10 = 0 @integer 20, 30, 40, 50, 60, 70, 80, 90, 100, 1000, 10000, 100000, 1000000, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"hi": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"hr": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.2~0.4, 1.2~1.4, 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100.2, 1000.2, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"hsb": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 100 = 1 or f % 100 = 1 @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-two": "v = 0 and i % 100 = 2 or f % 100 = 2 @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, … @decimal 0.2, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2, 10.2, 100.2, 1000.2, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 100 = 3..4 or f % 100 = 3..4 @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, … @decimal 0.3, 0.4, 1.3, 1.4, 2.3, 2.4, 3.3, 3.4, 4.3, 4.4, 5.3, 5.4, 6.3, 6.4, 7.3, 7.4, 10.3, 100.3, 1000.3, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"hu": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"hy": {
|
|
||||||
"pluralRule-count-one": "i = 0,1 @integer 0, 1 @decimal 0.0~1.5",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ia": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ig": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ii": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"in": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"io": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"is": {
|
|
||||||
"pluralRule-count-one": "t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1~1.6, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"it": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"iu": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"iw": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-two": "i = 2 and v = 0 @integer 2",
|
|
||||||
"pluralRule-count-many": "v = 0 and n != 0..10 and n % 10 = 0 @integer 20, 30, 40, 50, 60, 70, 80, 90, 100, 1000, 10000, 100000, 1000000, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ja": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"jbo": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"jgo": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ji": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"jmc": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"jv": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"jw": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ka": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kab": {
|
|
||||||
"pluralRule-count-one": "i = 0,1 @integer 0, 1 @decimal 0.0~1.5",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kaj": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kcg": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kde": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kea": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kk": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kkj": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kl": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"km": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kn": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ko": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ks": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ksb": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ksh": {
|
|
||||||
"pluralRule-count-zero": "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000",
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ku": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"kw": {
|
|
||||||
"pluralRule-count-zero": "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000",
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n % 100 = 2,22,42,62,82 or n % 1000 = 0 and n % 100000 = 1000..20000,40000,60000,80000 or n != 0 and n % 1000000 = 100000 @integer 2, 22, 42, 62, 82, 102, 122, 142, 1000, 10000, 100000, … @decimal 2.0, 22.0, 42.0, 62.0, 82.0, 102.0, 122.0, 142.0, 1000.0, 10000.0, 100000.0, …",
|
|
||||||
"pluralRule-count-few": "n % 100 = 3,23,43,63,83 @integer 3, 23, 43, 63, 83, 103, 123, 143, 1003, … @decimal 3.0, 23.0, 43.0, 63.0, 83.0, 103.0, 123.0, 143.0, 1003.0, …",
|
|
||||||
"pluralRule-count-many": "n != 1 and n % 100 = 1,21,41,61,81 @integer 21, 41, 61, 81, 101, 121, 141, 161, 1001, … @decimal 21.0, 41.0, 61.0, 81.0, 101.0, 121.0, 141.0, 161.0, 1001.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 4~19, 100, 1004, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.1, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ky": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"lag": {
|
|
||||||
"pluralRule-count-zero": "n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000",
|
|
||||||
"pluralRule-count-one": "i = 0,1 and n != 0 @integer 1 @decimal 0.1~1.6",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"lb": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"lg": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"lkt": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ln": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"lo": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"lt": {
|
|
||||||
"pluralRule-count-one": "n % 10 = 1 and n % 100 != 11..19 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, …",
|
|
||||||
"pluralRule-count-few": "n % 10 = 2..9 and n % 100 != 11..19 @integer 2~9, 22~29, 102, 1002, … @decimal 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 22.0, 102.0, 1002.0, …",
|
|
||||||
"pluralRule-count-many": "f != 0 @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"lv": {
|
|
||||||
"pluralRule-count-zero": "n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19 @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-one": "n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.0, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-other": " @integer 2~9, 22~29, 102, 1002, … @decimal 0.2~0.9, 1.2~1.9, 10.2, 100.2, 1000.2, …"
|
|
||||||
},
|
|
||||||
"mas": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"mg": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"mgo": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"mk": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.2~1.0, 1.2~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ml": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"mn": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"mo": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-few": "v != 0 or n = 0 or n % 100 = 2..19 @integer 0, 2~16, 102, 1002, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 20~35, 100, 1000, 10000, 100000, 1000000, …"
|
|
||||||
},
|
|
||||||
"mr": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"mt": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-few": "n = 0 or n % 100 = 2..10 @integer 0, 2~10, 102~107, 1002, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 102.0, 1002.0, …",
|
|
||||||
"pluralRule-count-many": "n % 100 = 11..19 @integer 11~19, 111~117, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 20~35, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"my": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nah": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"naq": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nb": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nd": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ne": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nl": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nn": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nnh": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"no": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nqo": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nr": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nso": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ny": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"nyn": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"om": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"or": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"os": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"osa": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"pa": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"pap": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"pcm": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"pl": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …",
|
|
||||||
"pluralRule-count-many": "v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …",
|
|
||||||
"pluralRule-count-other": " @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"prg": {
|
|
||||||
"pluralRule-count-zero": "n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19 @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-one": "n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.0, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-other": " @integer 2~9, 22~29, 102, 1002, … @decimal 0.2~0.9, 1.2~1.9, 10.2, 100.2, 1000.2, …"
|
|
||||||
},
|
|
||||||
"ps": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"pt": {
|
|
||||||
"pluralRule-count-one": "i = 0..1 @integer 0, 1 @decimal 0.0~1.5",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"pt-PT": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"rm": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ro": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-few": "v != 0 or n = 0 or n % 100 = 2..19 @integer 0, 2~16, 102, 1002, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 20~35, 100, 1000, 10000, 100000, 1000000, …"
|
|
||||||
},
|
|
||||||
"rof": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ru": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 10 = 1 and i % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …",
|
|
||||||
"pluralRule-count-many": "v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …",
|
|
||||||
"pluralRule-count-other": " @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"rwk": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sah": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"saq": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sat": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sc": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"scn": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sd": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sdh": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"se": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"seh": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ses": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sg": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sh": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.2~0.4, 1.2~1.4, 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100.2, 1000.2, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"shi": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-few": "n = 2..10 @integer 2~10 @decimal 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 2.00, 3.00, 4.00, 5.00, 6.00, 7.00, 8.00",
|
|
||||||
"pluralRule-count-other": " @integer 11~26, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~1.9, 2.1~2.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"si": {
|
|
||||||
"pluralRule-count-one": "n = 0,1 or i = 0 and f = 1 @integer 0, 1 @decimal 0.0, 0.1, 1.0, 0.00, 0.01, 1.00, 0.000, 0.001, 1.000, 0.0000, 0.0001, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.2~0.9, 1.1~1.8, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sk": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-few": "i = 2..4 and v = 0 @integer 2~4",
|
|
||||||
"pluralRule-count-many": "v != 0 @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …"
|
|
||||||
},
|
|
||||||
"sl": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 100 = 1 @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, …",
|
|
||||||
"pluralRule-count-two": "v = 0 and i % 100 = 2 @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 100 = 3..4 or v != 0 @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …"
|
|
||||||
},
|
|
||||||
"sma": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"smi": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"smj": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"smn": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sms": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-two": "n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sn": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"so": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sq": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sr": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.2~0.4, 1.2~1.4, 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100.2, 1000.2, …",
|
|
||||||
"pluralRule-count-other": " @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ss": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ssy": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"st": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"su": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sv": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"sw": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"syr": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ta": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"te": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"teo": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"th": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ti": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"tig": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"tk": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"tl": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9 @integer 0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.3, 0.5, 0.7, 0.8, 1.0~1.3, 1.5, 1.7, 1.8, 2.0, 2.1, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …",
|
|
||||||
"pluralRule-count-other": " @integer 4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, … @decimal 0.4, 0.6, 0.9, 1.4, 1.6, 1.9, 2.4, 2.6, 10.4, 100.4, 1000.4, …"
|
|
||||||
},
|
|
||||||
"tn": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"tr": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ts": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"tzm": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 or n = 11..99 @integer 0, 1, 11~24 @decimal 0.0, 1.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0",
|
|
||||||
"pluralRule-count-other": " @integer 2~10, 100~106, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ug": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"uk": {
|
|
||||||
"pluralRule-count-one": "v = 0 and i % 10 = 1 and i % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …",
|
|
||||||
"pluralRule-count-few": "v = 0 and i % 10 = 2..4 and i % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …",
|
|
||||||
"pluralRule-count-many": "v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …",
|
|
||||||
"pluralRule-count-other": " @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ur": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"uz": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"ve": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"vi": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"vo": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"vun": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"wa": {
|
|
||||||
"pluralRule-count-one": "n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"wae": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"wo": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"xh": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"xog": {
|
|
||||||
"pluralRule-count-one": "n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"yi": {
|
|
||||||
"pluralRule-count-one": "i = 1 and v = 0 @integer 1",
|
|
||||||
"pluralRule-count-other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"yo": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"yue": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"zh": {
|
|
||||||
"pluralRule-count-other": " @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
},
|
|
||||||
"zu": {
|
|
||||||
"pluralRule-count-one": "i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04",
|
|
||||||
"pluralRule-count-other": " @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
6642
testfiles/dict/video_duration_samples.json
Normal file
6642
testfiles/dict/video_duration_samples.json
Normal file
File diff suppressed because it is too large
Load diff
Reference in a new issue