feat: internationalization (#518)

Adds `languages` option to specify localization language priority.
Built in languages can be found in `scripts/uosc_shared/intl`.
This commit is contained in:
natural-harmonia-gropius
2023-04-24 17:37:58 +08:00
committed by GitHub
parent f274101fbd
commit 3c70ffd927
7 changed files with 156 additions and 34 deletions

View File

@@ -219,3 +219,9 @@ chapter_ranges=openings:30abf964,endings:30abf964,ads:c54e4e80
# Add alternative lua patterns to identify beginnings of simple chapter ranges (except for `ads`)
# Syntax: `{type}:{pattern}[,{patternN}][;{type}:{pattern}[,{patternN}]]`
chapter_range_patterns=openings:オープニング;endings:エンディング
# Localization language priority from highest to lowest.
# Built in languages can be found in `uosc_shared/intl`.
# `slang` is a keyword to inherit values from `--slang` mpv config.
# Supports paths to custom json files: `languages=~~/custom.json,slang,en`
languages=slang,en

View File

@@ -109,6 +109,7 @@ defaults = {
adjust_osd_margins = true,
chapter_ranges = 'openings:30abf964,endings:30abf964,ads:c54e4e80',
chapter_range_patterns = 'openings:オープニング;endings:エンディング',
languages = 'slang,en',
}
options = table_shallow_copy(defaults)
opt.read_options(options, 'uosc')
@@ -131,37 +132,40 @@ if options.autoload then mp.commandv('set', 'keep-open-pause', 'no') end
fg, bg = serialize_rgba(options.foreground).color, serialize_rgba(options.background).color
fgt, bgt = serialize_rgba(options.foreground_text).color, serialize_rgba(options.background_text).color
--[[ INTERNATIONALIZATION ]]
t = require('uosc_shared/lib/intl')
--[[ CONFIG ]]
function create_default_menu()
return {
{title = 'Subtitles', value = 'script-binding uosc/subtitles'},
{title = 'Audio tracks', value = 'script-binding uosc/audio'},
{title = 'Stream quality', value = 'script-binding uosc/stream-quality'},
{title = 'Playlist', value = 'script-binding uosc/items'},
{title = 'Chapters', value = 'script-binding uosc/chapters'},
{title = 'Navigation', items = {
{title = 'Next', hint = 'playlist or file', value = 'script-binding uosc/next'},
{title = 'Prev', hint = 'playlist or file', value = 'script-binding uosc/prev'},
{title = 'Delete file & Next', value = 'script-binding uosc/delete-file-next'},
{title = 'Delete file & Prev', value = 'script-binding uosc/delete-file-prev'},
{title = 'Delete file & Quit', value = 'script-binding uosc/delete-file-quit'},
{title = 'Open file', value = 'script-binding uosc/open-file'},
{title = t('Subtitles'), value = 'script-binding uosc/subtitles'},
{title = t('Audio tracks'), value = 'script-binding uosc/audio'},
{title = t('Stream quality'), value = 'script-binding uosc/stream-quality'},
{title = t('Playlist'), value = 'script-binding uosc/items'},
{title = t('Chapters'), value = 'script-binding uosc/chapters'},
{title = t('Navigation'), items = {
{title = t('Next'), hint = t('playlist or file'), value = 'script-binding uosc/next'},
{title = t('Prev'), hint = t('playlist or file'), value = 'script-binding uosc/prev'},
{title = t('Delete file & Next'), value = 'script-binding uosc/delete-file-next'},
{title = t('Delete file & Prev'), value = 'script-binding uosc/delete-file-prev'},
{title = t('Delete file & Quit'), value = 'script-binding uosc/delete-file-quit'},
{title = t('Open file'), value = 'script-binding uosc/open-file'},
},},
{title = 'Utils', items = {
{title = 'Aspect ratio', items = {
{title = 'Default', value = 'set video-aspect-override "-1"'},
{title = t('Utils'), items = {
{title = t('Aspect ratio'), items = {
{title = t('Default'), value = 'set video-aspect-override "-1"'},
{title = '16:9', value = 'set video-aspect-override "16:9"'},
{title = '4:3', value = 'set video-aspect-override "4:3"'},
{title = '2.35:1', value = 'set video-aspect-override "2.35:1"'},
},},
{title = 'Audio devices', value = 'script-binding uosc/audio-device'},
{title = 'Editions', value = 'script-binding uosc/editions'},
{title = 'Screenshot', value = 'async screenshot'},
{title = 'Show in directory', value = 'script-binding uosc/show-in-directory'},
{title = 'Open config folder', value = 'script-binding uosc/open-config-directory'},
{title = t('Audio devices'), value = 'script-binding uosc/audio-device'},
{title = t('Editions'), value = 'script-binding uosc/editions'},
{title = t('Screenshot'), value = 'async screenshot'},
{title = t('Show in directory'), value = 'script-binding uosc/show-in-directory'},
{title = t('Open config folder'), value = 'script-binding uosc/open-config-directory'},
},},
{title = 'Quit', value = 'quit'},
{title = t('Quit'), value = 'quit'},
}
end
@@ -222,6 +226,7 @@ config = {
local title_parts = split(title or '', ' *> *')
for index, title_part in ipairs(#title_parts > 0 and title_parts or {''}) do
title_part = t(title_part)
if index < #title_parts then
submenu_id = submenu_id .. title_part
@@ -987,7 +992,7 @@ bind_command('editions', create_self_updating_menu_opener({
local items = {}
for _, edition in ipairs(editions or {}) do
items[#items + 1] = {
title = edition.title or 'Edition',
title = edition.title or t('Edition'),
hint = tostring(edition.id + 1),
value = edition.id,
active = edition.id == current_id,

View File

@@ -65,7 +65,7 @@ function Controls:init()
for i, item in ipairs(items) do
local config = shorthands[item.config] and shorthands[item.config] or item.config
local config_tooltip = split(config, ' *%? *')
local tooltip = config_tooltip[2]
local tooltip = t(config_tooltip[2])
config = shorthands[config_tooltip[1]]
and split(shorthands[config_tooltip[1]], ' *%? *')[1] or config_tooltip[1]
local config_badge = split(config, ' *# *')

View File

@@ -799,7 +799,7 @@ function Menu:render()
if draw_title then
local title_ay = ay - self.item_height
local title_height = self.item_height - 3
menu.ass_safe_title = menu.ass_safe_title or ass_escape(menu.title)
menu.ass_safe_title = t(menu.ass_safe_title or ass_escape(menu.title))
-- Background
ass:rect(ax + 2, title_ay, bx - 2, title_ay + title_height, {

View File

@@ -0,0 +1,50 @@
{
"Aspect ratio": "纵横比",
"Audio": "音频",
"Audio device": "音频设备",
"Audio devices": "音频设备",
"Audio tracks": "音频轨道",
"Chapters": "章节",
"Default": "默认",
"Delete file & Next": "删除文件并播放下一个",
"Delete file & Prev": "删除文件并播放上一个",
"Delete file & Quit": "删除文件并退出",
"Disabled": "禁用",
"Drives": "驱动器",
"Edition": "版本",
"Editions": "版本",
"First": "第一个",
"Fullscreen": "全屏",
"Last": "最后一个",
"Load": "加载",
"Load audio": "加载音频",
"Load subtitles": "加载字幕",
"Load video": "加载视频",
"Loop file": "单个循环",
"Loop playlist": "列表循环",
"Menu": "菜单",
"Navigation": "导航",
"Next": "下一个",
"Open config folder": "打开设置文件夹",
"Open file": "打开文件",
"Playlist": "播放列表",
"Playlist/Files": "播放/文件列表",
"Prev": "上一个",
"Previous": "上一个",
"Quit": "退出",
"Screenshot": "截图",
"Show in directory": "打开所在文件夹",
"Shuffle": "乱序",
"Stream quality": "流媒体质量",
"Subtitles": "字幕",
"Track": "轨道",
"Utils": "工具",
"Video": "视频",
"default": "默认",
"drive": "磁盘",
"external": "外置",
"forced": "强制",
"open file": "打开文件",
"parent dir": "父文件夹",
"playlist or file": "播放列表或文件"
}

View File

@@ -0,0 +1,61 @@
local locale = {}
-- https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/supported-languages?pivots=store-installer-msix#list-of-supported-languages
function get_languages()
local languages = {}
for _, lang in ipairs(split(options.languages, ',')) do
if (lang == 'slang') then
local slang = mp.get_property_native('slang')
if slang then
itable_append(languages, slang)
end
else
itable_append(languages, { lang })
end
end
return languages
end
---@param path string
function get_locale_from_json(path)
local expand_path = mp.command_native({ 'expand-path', path })
local meta, meta_error = utils.file_info(expand_path)
if not meta or not meta.is_file then
return {}
end
local json_file = io.open(expand_path, 'r')
if not json_file then
return {}
end
local json = json_file:read('*all')
json_file:close()
return utils.parse_json(json)
end
function make_locale()
local translations = {}
for _, lang in ipairs(get_languages()) do
if (lang:match('.json$')) then
table_assign(translations, get_locale_from_json(lang))
else
table_assign(translations, get_locale_from_json('~~/scripts/uosc_shared/intl/' .. lang:lower() .. '.json'))
end
end
return translations
end
---@param text string
function t(text)
return locale[text] or text
end
locale = make_locale()
return t

View File

@@ -77,7 +77,7 @@ function create_select_tracklist_type_menu_opener(menu_title, track_type, track_
if load_command then
items[#items + 1] = {
title = 'Load', bold = true, italic = true, hint = 'open file', value = '{load}', separator = true,
title = t('Load'), bold = true, italic = true, hint = t('open file'), value = '{load}', separator = true,
}
end
@@ -91,7 +91,7 @@ function create_select_tracklist_type_menu_opener(menu_title, track_type, track_
-- If I'm mistaken and there is an active need for this, feel free to
-- open an issue.
if track_type == 'sub' then
disabled_item = {title = 'Disabled', italic = true, muted = true, hint = '', value = nil, active = true}
disabled_item = {title = t('Disabled'), italic = true, muted = true, hint = '', value = nil, active = true}
items[#items + 1] = disabled_item
end
@@ -108,12 +108,12 @@ function create_select_tracklist_type_menu_opener(menu_title, track_type, track_
h(track.codec)
if track['audio-channels'] then h(track['audio-channels'] .. ' channels') end
if track['demux-samplerate'] then h(string.format('%.3gkHz', track['demux-samplerate'] / 1000)) end
if track.forced then h('forced') end
if track.default then h('default') end
if track.external then h('external') end
if track.forced then h(t('forced')) end
if track.default then h(t('default')) end
if track.external then h(t('external')) end
items[#items + 1] = {
title = (track.title and track.title or 'Track ' .. track.id),
title = (track.title and track.title or t('Track') .. ' ' .. track.id),
hint = table.concat(hint_values, ', '),
value = track.id,
active = track.selected,
@@ -181,10 +181,10 @@ function open_file_navigation_menu(directory_path, handle_select, opts)
if is_root then
if state.platform == 'windows' then
items[#items + 1] = {title = '..', hint = 'Drives', value = '{drives}', separator = true}
items[#items + 1] = {title = '..', hint = t('Drives'), value = '{drives}', separator = true}
end
else
items[#items + 1] = {title = '..', hint = 'parent dir', value = directory.dirname, separator = true}
items[#items + 1] = {title = '..', hint = t('parent dir'), value = directory.dirname, separator = true}
end
local back_path = items[#items] and items[#items].value
@@ -276,7 +276,7 @@ function open_drives_menu(handle_select, opts)
if drive then
local drive_path = normalize_path(drive)
items[#items + 1] = {
title = drive, hint = 'drive', value = drive_path, active = opts.active_path == drive_path,
title = drive, hint = t('drive'), value = drive_path, active = opts.active_path == drive_path,
}
if opts.selected_path == drive_path then selected_index = #items end
end
@@ -286,7 +286,7 @@ function open_drives_menu(handle_select, opts)
end
return Menu:open(
{type = opts.type, title = opts.title or 'Drives', items = items, selected_index = selected_index},
{type = opts.type, title = opts.title or t('Drives'), items = items, selected_index = selected_index},
handle_select
)
end